/**
 * 本项目用于AS608指纹模块使用，使用到GPIO5(D1)引脚作为模拟输出TX引脚
 */
#include <SoftwareSerial.h>
#include <FS.h>
#include <U8g2lib.h>                       //U8g2lib库
#include <Wire.h>                          //Wire库
#include <Ticker.h>                        //Ticker库
#include <ESP8266WebServer.h>
#include <WiFiManager.h> 
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#define PRODUCT_ID    "TOP" //服务器用户名
#define API_KEY    "tx6WM==zmW21Z2pt4susBRlHMuY="//用户密码
#define DEVICE_ID "door_device"//设备名
#define TOPIC     "door_control"//订阅主题
//
bool door_clock=false;
//
WiFiClient wifiClient;
int count = 0; //ticker1控制 数据上传下发的间隔时间（s）
PubSubClient mqttClient(wifiClient);
const char* mqttServer = "*******";//服务器地址
const uint16_t mqttPort = 1883;//mqtt接口端口
char msgJson[75];//存json下发信息数据
char msg_buf[200];//存json上传数据及标识位
SoftwareSerial  mySerial(D3,D4);
const unsigned char welcome[] = {0X30,0X01,0X00,0X30,0X00,0X40,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7C,0X00,0X00,0XC0,0X9F,0X1F,
0XFF,0X01,0X00,0XFC,0XC0,0X30,0X82,0X00,0X00,0X80,0X60,0X20,0XC2,0X00,0X00,0XC0,
0X20,0X20,0X62,0X00,0X00,0XC0,0X20,0X30,0X3A,0X00,0X00,0X40,0X30,0X10,0X0F,0X00,
0X00,0X40,0X10,0X18,0X03,0X00,0X00,0X40,0X20,0X0C,0X03,0X00,0X00,0X40,0XE0,0X07,
0X03,0X00,0X00,0X00,0XC0,0X01,0X02,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XE0,0X00,0X00,0X00,
0X00,0X00,0XF8,0X7F,0X00,0X00,0X00,0X00,0XFC,0X7F,0X00,0X00,0X00,0X00,0XFE,0XFF,
0X00,0X00,0X00,0X00,0XFF,0XFF,0X01,0X00,0X00,0X00,0XFF,0XFF,0X01,0X00,0X00,0X80,
0XFF,0XFC,0X03,0X00,0X00,0X80,0X1F,0XF0,0X03,0X00,0X00,0XC0,0X1F,0XE0,0X07,0X00,
0X00,0XC0,0X0F,0XC0,0X07,0X00,0X00,0XC0,0X0F,0XC0,0X07,0X00,0X00,0XC0,0X03,0X00,
0X07,0X00,0X00,0XC0,0X01,0X20,0X07,0X00,0X00,0XC0,0X7D,0XFC,0X02,0X00,0X00,0X80,
0X00,0X00,0X02,0X00,0X00,0X00,0X00,0X7A,0X03,0X00,0X00,0X00,0X18,0X10,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X80,0X02,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XE0,0X0F,0X00,0X00,
0X00,0X00,0X82,0X44,0X00,0X00,0X00,0XE0,0X07,0X60,0X00,0X00,0X00,0XF8,0X0F,0XF0,
0X00,0X00,0X00,0XFC,0X37,0XF0,0X03,0X00,0X00,0XFE,0XC7,0XFF,0X07,0X00,0X80,0XFF,
0X07,0XFF,0X1F,0X00,0XC0,0XFD,0X07,0X70,0X3F,0X00,0XF8,0XFF,0X03,0X70,0XFF,0X01,
0XFE,0XFF,0X07,0X70,0XFE,0X0F,0XFF,0XFF,0X09,0X78,0XFE,0X1F,0XFF,0XFF,0X03,0X7C,
0XFC,0X3F,0XFF,0XFF,0X23,0X7E,0XFC,0X3F,0XFF,0XFF,0XC3,0X3F,0XF8,0X7F,0XFF,0XFF,
0X87,0X3F,0XF8,0XFF,0XFF,0XFF,0X07,0X18,0XF8,0XFF,0XFF,0XFF,0X07,0X00,0XF8,0XFF,
0XFF,0XFF,0X07,0X00,0XF8,0XFF,0XFF,0XFF,0X0F,0X00,0XF8,0XFF,0XFF,0XFF,0X3F,0X00,
0XF8,0XFF,0XFF,0XFF,0XDF,0X00,0XF8,0XFF,0XFF,0XFF,0X1F,0X00,0XF8,0XFF,0XFF,0XFF,
0X1F,0X00,0XF0,0XFF,0XFF,0XFF,0X1F,0X00,0XF0,0XFF,0XFF,0XFF,0X1F,0X00,0XF0,0XFF,};
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, SCL, SDA, U8X8_PIN_NONE);//D7 GPIO13 D8 GPIO15// D1 D2
byte buf_getfinger[20]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x01,0x00,0x05};//录一个指纹
byte buf_clear[20]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x0d,0x00,0x11};//清空指纹信息
byte buf_delete[20]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x07,0x0c,0x00,0x01,0x11};//删除指纹信息 本条只是模板需要更改
byte buf_finger_char[20]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0X04,0x02,0x01,0x00,0x08};//生成指纹特征
byte buf_identity[20]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0X03,0x11,0x00,0x15};//工作模式，读取指纹数据进行比对  返回ID
byte buf_getlist[20]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x1f,0x00,0x23};//获取指纹目录，数据是001010101格式 不是很好用，所以采用了新方法使用（在录入指纹时编写记事本文件存储）
byte buf_add[20]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0X03,0x10,0x00,0x14};//注册添加指纹 返回ID
byte buf_logo[6]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF};
byte buf[32];
String file_name = "/AS608/users.txt"; //被读取的文件位置和名称
String file_list="";
int work_index=0;
void setup() {
    Serial.begin(57600);
    mySerial.begin(57600);
    //不采用外部按钮控制，不够灵活且占用引脚，改为软件控制，空出的引脚可作为门锁开关电机控制
    /*pinMode(D5,INPUT_PULLUP);//正常工作模式
    pinMode(D6,INPUT_PULLUP);//添加模式
    pinMode(D7,INPUT_PULLUP);//删除模式
    pinMode(D8,INPUT_PULLUP);//查询指纹列表
    digitalWrite(D6,HIGH);
    digitalWrite(D7,HIGH);
    digitalWrite(D5,HIGH);
    digitalWrite(D8,HIGH);*/
    //控制电机
    pinMode(D5,OUTPUT);
    pinMode(D6,OUTPUT);
    pinMode(D7,OUTPUT);
    pinMode(D8,OUTPUT);
     pinMode(D0,OUTPUT);//
    //pinMode(10,OUTPUT);
    //pinMode(9,OUTPUT);
    // WiFi.mode(WIFI_STA);
    WiFiManager wifiManager;
  // 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称
    wifiManager.autoConnect("门锁WiFi设置");
     mqttClient.setServer(mqttServer, mqttPort);
  // 设置MQTT订阅回调函数
  mqttClient.setCallback(receiveCallback);
  connectMQTTServer();
    if(SPIFFS.begin()){ // 启动SPIFFS
    Serial.println("SPIFFS Started.");
  } else {
    Serial.println("SPIFFS Failed to Start.");
  }
   u8g2.begin();
  //允许显示UTF8字符
  u8g2.enableUTF8Print();
  //文本的参考点为左上角
  u8g2.setFontPosTop();
  u8g2.clearBuffer();
  u8g2.drawXBMP(40, 0, 48, 64, welcome);
  u8g2.sendBuffer();
  delay(1000);
}
void connectMQTTServer() {
  String clientId = DEVICE_ID;
  String productId = PRODUCT_ID;
  String apiKey = API_KEY;
  // 连接MQTT服务器
  if (mqttClient.connect(clientId.c_str(), productId.c_str(), apiKey.c_str())) {
    Serial.println("MQTT Server Connected.");
    Serial.println("Server Address: ");
    Serial.println(mqttServer);
    Serial.println("ClientId:");
    Serial.println(clientId);
    subscribeTopic(); // 订阅指定主题
  } else {
    Serial.print("MQTT Server Connect Failed. Client State:");
    Serial.println(mqttClient.state());
    delay(1000);
  }
}
// 订阅指定主题
void subscribeTopic() {
  // 这么做是为确保不同设备使用同一个MQTT服务器测试消息订阅时，所订阅的主题名称不同
  String topicString = TOPIC;
  char subTopic[topicString.length() + 1];
  strcpy(subTopic, topicString.c_str());

  // 通过串口监视器输出是否成功订阅主题以及订阅的主题名称
  if (mqttClient.subscribe(subTopic)) {
    Serial.println("Subscrib Topic:");
    Serial.println(subTopic);
  } else {
    Serial.print("Subscribe Fail...");
  }
}
//获取下发指令topic 指定主题 payload 下发信息，以字节存储 length 下发信息长度
void receiveCallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message Received [");
  Serial.print(topic);
  Serial.print("] ");
  String receiveMessage;
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
    receiveMessage += (char)payload[i];
  }
  Serial.println("----" + receiveMessage + "----");
  Serial.print("Message Length(Bytes) ");
  Serial.println(length);
  ///
  StaticJsonDocument<128> doc;
  deserializeJson(doc, receiveMessage);
  String door_control = doc["door_cmd"]; // 
  String door_message = doc["message"];
  //Serial.print(control);
  if(door_control=="add")
  {
    Serial.println("添加指纹模式");
    work_index=1;
    String msg;
    StaticJsonDocument<48> doc;

    doc["door_msg"] = "add ok";

    serializeJson(doc, msg);
    char publishMsg[msg.length() + 1];
    strcpy(publishMsg, msg.c_str());
    int lens = strlen(publishMsg);
    mqttClient.publish("getMessage", publishMsg, lens);   
  }else if(door_control=="delete")
  {
    Serial.println("删除指纹模式");
    work_index=2;
    String msg;
    StaticJsonDocument<48> doc;

    doc["door_msg"] = "delete ok";

    serializeJson(doc, msg);
    char publishMsg[msg.length() + 1];
    strcpy(publishMsg, msg.c_str());
    int lens = strlen(publishMsg);
    mqttClient.publish("getMessage", publishMsg, lens);   
  }else if(door_control=="list")
  {
    Serial.println("查看指纹模式");
    work_index=3;
    File dataFile = SPIFFS.open(file_name, "r"); 
    String user_list;
    String msg;
    for(int i=0;i<dataFile.size();i++)
  {
    
      user_list+=(char)dataFile.read();
    
    
  }
    dataFile.close(); 
    StaticJsonDocument<80> doc;

    doc["door_msg"] = user_list;

    serializeJson(doc, msg);
    char publishMsg[msg.length() + 1];
    strcpy(publishMsg, msg.c_str());
    int lens = strlen(publishMsg);
    mqttClient.publish("getMessage", publishMsg, lens);    
  }else if(door_control=="work")
  {
    Serial.println("工作模式");
    work_index=0;
    String msg;
    StaticJsonDocument<48> doc;

    doc["door_msg"] = "work ok";

    serializeJson(doc, msg);
    char publishMsg[msg.length() + 1];
    strcpy(publishMsg, msg.c_str());
    int lens = strlen(publishMsg);
    mqttClient.publish("getMessage", publishMsg, lens);
  }
}
void finger_add(){
   
  u8g2.setFontPosTop();
  u8g2.setFont(u8g2_font_crox2hb_tr);
  u8g2.clearBuffer();
  u8g2.drawStr(30, 0, "add finger");
  u8g2.drawLine(0, 14, 128, 14);
  u8g2.sendBuffer();
  mySerial.write(buf_add, 12);
  u8g2.setFont(u8g2_font_crox2hb_tr);
  u8g2.drawStr(0, 20, "pressing");
  delay(2000);
  int i = 0;
 /* while (Serial.available()<6) {
    delay(1000);
    u8g2.drawStr(i+64, 20, ".");
    Serial.println("正在连接");
    i = i + 5;
    u8g2.sendBuffer();
  }*/
   if(Serial.available()>6){
       size_t len = Serial.available();  //返回可读数据的长度
       uint8_t sbuf[len];
       Serial.readBytes(sbuf, len);
       if(sbuf[6]==0x07){
        //Serial.println("验证指纹");
        if(sbuf[9]==0x00){
        u8g2.drawStr(0, 34, "ok!");
        u8g2.drawStr(0, 48, "new ID:");
        u8g2.setCursor(80, 48);
        u8g2.print(sbuf[10]);
        u8g2.print(sbuf[11]);
        u8g2.sendBuffer();
        Serial.println("注册成功，存在");
        Serial.print("ID：");
        Serial.print(sbuf[10]);
        Serial.print(sbuf[11]);
        Serial.println();
        File dataFile = SPIFFS.open(file_name, "a");
        dataFile.print("用户");
        dataFile.print(sbuf[10]);
        dataFile.println(sbuf[11]); 
        dataFile.println(",");        
        dataFile.close();                         
       }else if(sbuf[9]==0x01){
        Serial.println("识别错误，请重新按下指纹");
        u8g2.drawStr(0, 20, "error!");
         u8g2.drawStr(0, 22, "press again!");
         u8g2.sendBuffer();
       }else if(sbuf[9]==0x1e){
        Serial.println("注册失败！");
        u8g2.drawStr(0, 20, "fail!");
        u8g2.sendBuffer();
       }
         delay(1000);
       }
     
      }
   
}
void getlist(){
  //读取文件内容并且通过串口监视器输出文件信息
  u8g2.setFontPosTop();
  u8g2.setFont(u8g2_font_crox2hb_tr);
  u8g2.clearBuffer();
  u8g2.drawStr(30, 0, "finger list");
  u8g2.drawLine(0, 14, 128, 14);
  u8g2.sendBuffer();
  u8g2.setCursor(0, 26);
  File dataFile = SPIFFS.open(file_name, "r"); 
  String user_list;
  for(int i=0;i<dataFile.size();i++)
  {
    
    user_list+=(char)dataFile.read();
    
    
  }
  u8g2.print(user_list);
  Serial.print(user_list);  
  u8g2.sendBuffer();
  dataFile.close(); 
  delay(1000);
}
void finger_clear(){
  mySerial.write(buf_clear, 12);
  u8g2.setFontPosTop();
  u8g2.setFont(u8g2_font_crox2hb_tr);
  u8g2.clearBuffer();
  u8g2.drawStr(30, 0, "finger clear");
  u8g2.drawLine(0, 14, 128, 14);
  u8g2.sendBuffer();
  u8g2.setCursor(0, 26);
   delay(1000);
  if(Serial.available()>6){
       size_t len = Serial.available();  //返回可读数据的长度
       uint8_t sbuf[len];
       Serial.readBytes(sbuf, len);
       /*for(int i=0; i<len; i++){      // 然后通过串口监视器输出readBytes
        Serial.print(sbuf[i],HEX);          // 函数所读取的信息
       }*/
       if(sbuf[6]==0x07){
        //Serial.println("验证指纹");
        if(sbuf[9]==0x00){
        u8g2.println("success!");
        u8g2.sendBuffer();
        Serial.println("删除成功！");
        File dataFile = SPIFFS.open(file_name, "w");
        dataFile.print("");
        dataFile.close();    
        Serial.println();
       }else if(sbuf[9]==0x01){
        Serial.println("包错误！");
        u8g2.println("error!");
        u8g2.sendBuffer();
       }else if(sbuf[9]==0x11){
        u8g2.println("fail!");
        u8g2.sendBuffer();
        Serial.println("删除失败！");
       }
         delay(2000);
       }
    
       }
}
void work(){
  mySerial.write(buf_identity, 12);
  u8g2.setFontPosTop();
  u8g2.setFont(u8g2_font_crox2hb_tr);
  u8g2.clearBuffer();
  u8g2.drawStr(10, 0, "finger working");
  u8g2.drawLine(0, 14, 128, 14);
  u8g2.drawStr(0, 18, "press checking!");
  u8g2.sendBuffer();
  u8g2.setCursor(0, 32);
  delay(1000);
  if(Serial.available()>6){
       size_t len = Serial.available();  //返回可读数据的长度
       uint8_t sbuf[len];
       Serial.readBytes(sbuf, len);
       /*for(int i=0; i<len; i++){      // 然后通过串口监视器输出readBytes
        Serial.print(sbuf[i],HEX);          // 函数所读取的信息
       }*/
       if(sbuf[6]==0x07){
        //Serial.println("验证指纹");
        if(sbuf[9]==0x00){
        u8g2.print("wecome!");
        u8g2.setCursor(0, 46);
        u8g2.print("ID：");
        u8g2.print(sbuf[10]);
        u8g2.print(sbuf[11]);
        u8g2.sendBuffer();
        Serial.println("识别成功，存在");
        Serial.print("ID：");
        Serial.print(sbuf[10]);
        Serial.print(sbuf[11]);
        Serial.println();
        /**
         * 添加开门模块
         * 可改为步进电机更为精确和稳定
         * 暂时手里只有个N20减速电机，加个驱动板拿来用用
         */
       
        for(int i=0;i<200;i++){
        delay(2);
        digitalWrite(D5, HIGH);
        digitalWrite(D6, HIGH);
        digitalWrite(D7, LOW);
        digitalWrite(D8, LOW);
        delay(2);
        digitalWrite(D5, LOW);
        digitalWrite(D6, HIGH);
        digitalWrite(D7, HIGH);
        digitalWrite(D8, LOW);
        delay(2);
        digitalWrite(D5, LOW);
        digitalWrite(D6, LOW);
        digitalWrite(D7, HIGH);
        digitalWrite(D8, HIGH);
        delay(2);
        digitalWrite(D5, HIGH);
        digitalWrite(D6, LOW);
        digitalWrite(D7, LOW);
        digitalWrite(D8, HIGH);
        }
        
        for(int i=0;i<200;i++){
        delay(2);
        digitalWrite(D5, HIGH);
        digitalWrite(D6, HIGH);
        digitalWrite(D7, LOW);
        digitalWrite(D8, LOW);
        delay(2);
        digitalWrite(D5, HIGH);
        digitalWrite(D6, LOW);
        digitalWrite(D7, LOW);
        digitalWrite(D8, HIGH);
        delay(2);
        digitalWrite(D5, LOW);
        digitalWrite(D6, LOW);
        digitalWrite(D7, HIGH);
        digitalWrite(D8, HIGH);
        delay(2);
        digitalWrite(D5, LOW);
        digitalWrite(D6, HIGH);
        digitalWrite(D7, HIGH);
        digitalWrite(D8, LOW);
        }
       }else if(sbuf[9]==0x01){
        u8g2.print("error!");
        u8g2.setCursor(0, 46);
        u8g2.print("press again!");
        u8g2.sendBuffer();
        Serial.println("识别错误，请重新按下指纹");
       }else if(sbuf[9]==0x09){
        u8g2.print("no user!");
        u8g2.setCursor(0, 46);
        u8g2.print("press again!");
        u8g2.sendBuffer();
        Serial.println("没有检索到该用户，请重新按下指纹");
       }
       delay(1000);
       }
       
       }
}
void loop() {
 
  //mySerial.listen(); 
  //软件控制方式
  if (mqttClient.connected()) { // 如果开发板成功连接服务器
    // 每隔2秒钟发布一次信息
    // 保持心跳 若电机正在运转，暂时不发信息（由于mcu没有多线程，不能同时运转电机和上传下发数据）
      mqttClient.loop();
  } else {                  // 如果开发板未能成功连接服务器
    connectMQTTServer();    // 则尝试连接服务器
  }
  //硬件控制方式
  /*if(digitalRead(D5)==LOW){
     Serial.println("工作模式0");
    work_index=0;//工作模式
  }else if(digitalRead(D6)==LOW){
     Serial.println("工作模式1");
    work_index=1;//添加模式
  }else if(digitalRead(D7)==LOW){
     Serial.println("工作模式2");
    work_index=2;//删除模式
  }else if(digitalRead(D8)==LOW){
     Serial.println("工作模式3");
    work_index=3;//查询模式
  }*/
  if(work_index==0){
    work();
  }else if(work_index==1){
    finger_add();
  }else if(work_index==2){
    finger_clear();
  }else if(work_index==3){
    getlist();
  }
  
}
